package com.amt.olcacon.controller.process;

import cn.hutool.json.JSONUtil;
import com.amt.olcacon.db.DerbyConfig;
import com.amt.olcacon.service.IProductProcessService;
import com.amt.olcacon.vo.GlobalResponse;
import com.amt.olcacon.vo.request.ProcessRequest;
import com.amt.olcacon.vo.request.ProcessRequestList;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.openlca.core.DataDir;
import org.openlca.core.database.Derby;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.database.UnitDao;
import org.openlca.core.model.*;
import org.openlca.core.model.Process;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * @Author: yangyusheng
 * @Date: 2025/05/28
 */
@Slf4j
@RestController
@RequestMapping("/olca/process")
public class ProcessController {

    private final IProductProcessService productProcessService;

    public ProcessController(IProductProcessService productProcessService) {
        this.productProcessService = productProcessService;
    }

    @PostMapping("/add")
    public GlobalResponse<Long> add(@RequestBody ProcessRequest request) {
        log.info("ProcessController#add request:{}", JSONUtil.toJsonStr(request));
        try (var db = DataDir.get().openDatabase(DerbyConfig.DBNAME)) {
            Process process = saveProcess(request, db);
            db.close();
            return GlobalResponse.of(process.id);
        } catch (Exception e) {
            log.error("ProcessController#add error", e);
        }
        return GlobalResponse.error("add process error");
    }

    @PostMapping("/addBatch")
    public GlobalResponse<Long> addBatch(@RequestBody ProcessRequestList requestList) {
        log.info("ProcessController#addBatch request:{}", JSONUtil.toJsonStr(requestList));
        try (var db = DataDir.get().openDatabase(DerbyConfig.DBNAME)) {
            requestList.getProcessRequestList().forEach(request -> saveProcess(request, db));

            db.close();
            return GlobalResponse.success();
        } catch (Exception e) {
            log.error("ProcessController#addBatch error", e);
        }
        return GlobalResponse.error("addBatch process error");
    }

    private static Process saveProcess(ProcessRequest request, Derby db) {
        Process process = Process.of(
                request.getName(),
                db.get(Flow.class, request.getFlowId())
        );
        process.id = request.getId();
        process.refId = request.getRefId();

        if (StringUtils.isNotBlank(request.getLocationRefId())) {
            process.location = db.get(Location.class, request.getLocationRefId());
        }
        if (StringUtils.isNotBlank(request.getDescription())) {
            process.description = request.getDescription();
        }
        process.processType = request.getProcessType();
        process.defaultAllocationMethod = request.getDefaultAllocationMethod();
        process.infrastructureProcess = request.getInfrastructureProcess();
        if (request.getLastInternalId() != null) {
            process.lastInternalId = request.getLastInternalId();
        }
        if (StringUtils.isNotBlank(request.getDqEntry())) {
            process.dqEntry = request.getDqEntry();
        }

        if (StringUtils.isNotBlank(request.getQuantitativeReferenceFlowRefId())) {
            process.quantitativeReference = Exchange.input(
                    db.get(Flow.class, request.getQuantitativeReferenceFlowRefId()),
                    request.getQuantitativeReferenceAmount()
            );
        }

        if (null != request.getInputIds()) {
            request.getInputIds().forEach((exchangeRequest) -> {
                var unitdao = new UnitDao(db);
                Exchange inputExchange = Exchange.of(db.get(Flow.class, exchangeRequest.getFlowId()), db.get(FlowProperty.class, exchangeRequest.getPropertyId()), unitdao.getForId(exchangeRequest.getUnitId()));
                inputExchange.amount = exchangeRequest.getAmount();
                inputExchange.isInput = true;

                // 修改不确定性为 正态分布（Normal）
                inputExchange.uncertainty = Uncertainty.normal(exchangeRequest.getAmount(), exchangeRequest.getAmount() * 0.05);
                log.info("inputExchange set uncertainty:{}", exchangeRequest.getAmount() * 0.05);

                if (exchangeRequest.getDefaultProviderId() != null) {
                    if (exchangeRequest.getDefaultProviderId() <= 63763210) {
                        Flow inputFlow = db.get(Process.class, exchangeRequest.getDefaultProviderId()).quantitativeReference.flow;
                        // 新建一个process，quantitativeReference是当前的输入输出项
                        Process exchangeRequestProcess = Process.of(
                                inputExchange.flow.name + UUID.randomUUID().toString(),
                                db.get(Flow.class, exchangeRequest.getFlowId())
                        );
                        // input 设置为 inputFlow , provider 是 exchangeRequest.getDefaultProviderId();
                        Exchange exchangeRequestProcessInput = Exchange.input(inputFlow, 1);
                        exchangeRequestProcessInput.defaultProviderId = exchangeRequest.getDefaultProviderId();
                        // input 设置为 inputFlow , provider 是 exchangeRequest.getDefaultProviderId();
                        // 修改 当前的process 的 default Provider 为 新建的processid
                        exchangeRequestProcess.add(exchangeRequestProcessInput);

                        var dao = new ProcessDao(db);
                        inputExchange.defaultProviderId = dao.insert(exchangeRequestProcess).id;
                    } else {
                        inputExchange.defaultProviderId = exchangeRequest.getDefaultProviderId();
                    }
                }
                process.add(inputExchange);
            });
        }

        if (null != request.getOutputIds()) {
            request.getOutputIds().forEach((exchangeRequest -> {

                //判断主产品
                if (!Objects.equals(exchangeRequest.getFlowId(), request.getFlowId())) {
                    log.info("当前执行的outputId是{}，请求参数的flowId是{}", exchangeRequest.getFlowId(), request.getFlowId());
                    var unitdao = new UnitDao(db);
                    Exchange outputExchange = Exchange.of(db.get(Flow.class, exchangeRequest.getFlowId()), db.get(FlowProperty.class, exchangeRequest.getPropertyId()), unitdao.getForId(exchangeRequest.getUnitId()));
                    outputExchange.amount = exchangeRequest.getAmount();
                    outputExchange.isInput = false;
                    if (exchangeRequest.getDefaultProviderId() != null) {
                        outputExchange.defaultProviderId = exchangeRequest.getDefaultProviderId();
                    }

                    // 修改不确定性为 正态分布（Normal）
                    outputExchange.uncertainty = Uncertainty.normal(exchangeRequest.getAmount(), exchangeRequest.getAmount() * 0.05);
                    log.info("outputExchange set uncertainty:{}", exchangeRequest.getAmount() * 0.05);

                    process.add(outputExchange);
                } else {
                    var unitdao = new UnitDao(db);
                    process.quantitativeReference.amount = exchangeRequest.getAmount();
                    process.quantitativeReference.unit = unitdao.getForId(exchangeRequest.getUnitId());
                    process.quantitativeReference.flowPropertyFactor = process.quantitativeReference.flow.getFactor(db.get(FlowProperty.class, exchangeRequest.getPropertyId()));
                    if (exchangeRequest.getDefaultProviderId() != null) {
                        process.quantitativeReference.defaultProviderId = exchangeRequest.getDefaultProviderId();
                    }
                }
            }));
        }


        // 持久化到数据库
        var dao = new ProcessDao(db);
        dao.insert(process);
        return process;
    }

    @PostMapping("/update")
    public GlobalResponse<Long> update(@RequestBody ProcessRequest request) {


        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        var dao = new ProcessDao(db);

        Process process = dao.getForId(request.getId());
        process.refId = request.getRefId();
        if (StringUtils.isNotBlank(request.getLocationRefId())) {
            process.location = db.get(Location.class, request.getLocationRefId());
        }
        if (StringUtils.isNotBlank(request.getDescription())) {
            process.description = request.getDescription();
        }
        process.processType = request.getProcessType();
        process.defaultAllocationMethod = request.getDefaultAllocationMethod();
        process.infrastructureProcess = request.getInfrastructureProcess();

        if (request.getLastInternalId() != null) {
            process.lastInternalId = request.getLastInternalId();
        }
        if (StringUtils.isNotBlank(request.getDqEntry())) {
            process.dqEntry = request.getDqEntry();
        }

        if (StringUtils.isNotBlank(request.getQuantitativeReferenceFlowRefId())) {
            process.quantitativeReference = Exchange.input(
                    db.get(Flow.class, request.getQuantitativeReferenceFlowRefId()),
                    request.getQuantitativeReferenceAmount()
            );
        }

        process.exchanges.clear();

        request.getInputIds().forEach((exchangeRequest) -> {
            Exchange inputExchange = Exchange.input(db.get(Flow.class, exchangeRequest.getFlowRefId()), exchangeRequest.getAmount());
            if (exchangeRequest.getDefaultProviderId() != null) {
                inputExchange.defaultProviderId = exchangeRequest.getDefaultProviderId();
            }
            if (exchangeRequest.getUnitId() != null) {
                var Unitdao = new UnitDao(db);
                inputExchange.unit = Unitdao.getForId(exchangeRequest.getUnitId());
            }
            process.add(inputExchange);
        });

        request.getOutputIds().forEach((exchangeRequest -> {
            Exchange outputExchange = Exchange.output(db.get(Flow.class, exchangeRequest.getFlowRefId()), exchangeRequest.getAmount());
            if (exchangeRequest.getDefaultProviderId() != null) {
                outputExchange.defaultProviderId = exchangeRequest.getDefaultProviderId();
            }
            if (exchangeRequest.getUnitId() != null) {
                var Unitdao = new UnitDao(db);
                outputExchange.unit = Unitdao.getForId(exchangeRequest.getUnitId());
            }
            process.add(outputExchange);
        }));

        // 持久化到数据库
        var result = dao.update(process);
        db.close();

        return GlobalResponse.of(result.id);
    }

    @GetMapping("/delete/{id}")
    public GlobalResponse<String> delete(@PathVariable("id") String id) {

        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        new ProcessDao(db).delete(Long.parseLong(id));
        db.close();

        return GlobalResponse.success();
    }

    @GetMapping("/get/{refId}")
    public GlobalResponse<Process> get(@PathVariable("refId") String refId) {

        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        var dao = new ProcessDao(db);
        Process process = dao.getForRefId(refId);
        db.close();
        if (process.category != null) {
            process.category = null;
        }
        process.exchanges.forEach(i -> {
            if (i.flow != null) {
                i.flow.category = null;
                i.flow.referenceFlowProperty = null;
                i.flow.flowPropertyFactors.clear();
            }
            i.flowPropertyFactor = null;
            i.currency = null;
            i.location = null;
            i.uncertainty = null;

        });
        process.location = null;
        process.quantitativeReference = null;
        process.exchangeDqSystem = null;
        process.documentation = null;

        return GlobalResponse.of(process);
    }

    @GetMapping("/getById/{id}")
    public GlobalResponse<Process> getById(@PathVariable("id") String id) {

        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        var dao = new ProcessDao(db);
        Process process = dao.getForId(Long.parseLong(id));
        db.close();
        if (process.category != null) {
            process.category = null;
        }
        process.exchanges.forEach(i -> {
            if (i.flow != null) {
                i.flow.category = null;
                i.flow.referenceFlowProperty = null;
                i.flow.flowPropertyFactors.clear();
            }
            i.flowPropertyFactor = null;
            i.currency = null;
            i.location = null;
            i.uncertainty = null;

        });
        process.location = null;
        process.quantitativeReference = null;
        process.exchangeDqSystem = null;
        process.documentation = null;

        return GlobalResponse.of(process);
    }

    @GetMapping("/getAll")
    public GlobalResponse<List<Process>> getAll() {

        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        var dao = new ProcessDao(db);
        List<Process> result = dao.getAll();
        db.close();
        result.forEach(row -> {
            row.category = null;
            row.exchanges = null;
            row.location = null;
            row.quantitativeReference = null;
            row.exchangeDqSystem = null;
            row.documentation = null;
        });
        return GlobalResponse.of(result);
    }

    /**
     * excel 导入
     */
    @GetMapping("/importExcel")
    public GlobalResponse<Boolean> importExcel(@RequestParam("filePath") String filePath, @RequestParam("currentUser") String currentUser) throws IOException {
        return productProcessService.importExcel(filePath, currentUser);
    }


    /**
     * excel 导入(多 sheet 版)
     */
    @GetMapping("/importExcelBatch")
    public GlobalResponse<Boolean> importExcelBatch(@RequestParam("filePath") String filePath, @RequestParam("currentUser") String currentUser) throws IOException {
        return productProcessService.importExcelBatch(filePath, currentUser);
    }

    @GetMapping("/checkExcelData")
    public GlobalResponse<String> checkExcelData(@RequestParam("filePath") String filePath) {
        return productProcessService.checkExcelData(filePath);
    }

    /**
     * 填报之后新增整个process树
     */
    @GetMapping("/addProcessTree")
    public GlobalResponse<Boolean> addProcessTree(@RequestParam("taskId") Long taskId) {
        return productProcessService.addProcessTree(taskId);
    }


    @GetMapping("/getReferenceFlowPropertyIdList")
    public List<Long> getReferenceFlowProperty(@RequestParam("id") Long id){
        try(var db = DataDir.get().openDatabase(DerbyConfig.DBNAME)) {
            Flow flow = new ProcessDao(db).getForId(id).quantitativeReference.flow;
            List<Long> flowPropertyIdList = flow.flowPropertyFactors.stream().map(i -> i.flowProperty.id).toList();
            log.info("flowPropertyIdList是 :{}",flowPropertyIdList);
            return flowPropertyIdList;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


}
